import './index.scss';

import classNames from 'classnames';
import React, { FC, MouseEventHandler, ReactNode } from 'react';

import ErrorIcon from '@/assets/icons/node-state--error.svg';
import RunningIcon from '@/assets/icons/node-state--running.svg';
import SuccessIcon from '@/assets/icons/node-state--success.svg';
import { colors } from '@/config/theme';
import { NODE_LIST, NODE_TYPE } from '@/constants/config';
import { useCurrentNode } from '@/hooks/useCurrentNode';
import { useGraph } from '@/hooks/useGraph';
import { useNodeResults } from '@/store/node-results';
import { useNodeVerificationResults } from '@/store/node-verification';

import { CreateNextNode } from './components/CreateNextNode';
import { NodeExecLogger } from './components/NodeExecLogger';
import { NodeVerificationLogger } from './components/NodeVerificationLogger';
import { BaseNodeProps } from './define';

interface BaseNodeContentProps {
  nodeName?: string;
  nodeType: keyof typeof NODE_TYPE;
  nodeNameEditable?: boolean;
  children?: React.ReactNode;
  onClick?: (e: React.MouseEvent) => void;
  className?: string;
}

export const BaseNodeContent: FC<BaseNodeContentProps> = memo((props) => {
  const node = NODE_LIST[props.nodeType!];
  const nodeName = props.nodeName || node?.nodeName || '';
  const Icon = node?.iconSvg || (() => null);

  return (
    <div onClick={props.onClick} className={classNames(['flex items-center base-node-content', props.className])}>
      <Icon width={18} height={18} className="ml-8" fill={colors.gray[400]} />
      <span className="label ml-8">{nodeName}</span>
      {props.children}
    </div>
  );
});

export const BaseNode = memo((props: BaseNodeProps<any>) => {
  const { node, status = 'INIT' } = props;
  const { interacting } = useGraph();
  const { activeNodeId, setActiveNodeId, currentNodeData } = useCurrentNode();
  const execResultsStore = useNodeResults();
  const verificationResultsStore = useNodeVerificationResults();
  const isFocus = activeNodeId === props.node?.id;
  const targetEl = useRef<HTMLElement | null>(null);
  const position = useRef('');
  const isCancelClick = useRef(false);
  const isInGraph = !!node;
  const targetNodeResult = execResultsStore.resultsOfList.find((item) => item.nodeId === props.node?.id);
  const finalStatus = targetNodeResult?.execStatus || status;

  const tempNodeName = props.nodeName || node?.getData()?.nodeName || currentNodeData?.nodeName;
  const finalNodeName = isInGraph === false ? NODE_LIST[props.nodeType!]?.nodeName : tempNodeName;

  const finalData: BaseNodeProps<any> = Object.assign(
    {
      node,
      graph: props.graph
    },
    currentNodeData,
    {
      nodeType: props.nodeType,
      nodeName: finalNodeName
    }
  );

  const getPosition = () => {
    const rect = targetEl.current?.getBoundingClientRect() || { x: 0, y: 0 };
    return [rect.x, rect.y].join('-');
  };

  const onMouseup = () => {
    isCancelClick.current = false;
    if (position.current !== getPosition()) {
      isCancelClick.current = true;
    }
  };

  const onMouseEnter = () => {
    // 获取该节点下的所有连接桩
    const ports = node?.getPorts() || [];
    ports.forEach((port) => {
      node?.setPortProp(port.id!, 'attrs/circle', {
        fill: '#fff',
        stroke: colors.primary[500]
      });
    });
  };

  // 鼠标离开矩形主区域的时候隐藏连接桩
  const onMouseLeave = () => {
    // 获取该节点下的所有连接桩
    const ports = node?.getPorts() || [];
    ports.forEach((port) => {
      node?.setPortProp(port.id!, 'attrs/circle', {
        fill: 'transparent',
        stroke: 'transparent'
      });
    });
  };

  const onMousedown: MouseEventHandler<HTMLElement> = (e) => {
    targetEl.current = e.target as HTMLElement;
    position.current = getPosition();
  };

  const renderBaseNodeContent = () => {
    const content: ReactNode[] = [
      <div className="node-actions" key="status">
        <span className="status">
          {finalStatus === 'FINISHED' && <SuccessIcon fill={colors.green[500]} />}
          {finalStatus === 'FAILED' && <ErrorIcon fill={colors.red[500]} />}
          {['RUNNING', 'WAITING'].includes(finalStatus) && <RunningIcon />}
        </span>
        {interacting && !execResultsStore.hasExecResult && (
          <CreateNextNode {...finalData} nodeType={props.nodeType} node={finalData.node} />
        )}
      </div>
    ];

    if (execResultsStore.hasExecResult) {
      if (finalStatus !== 'WAITING') {
        content.push(<NodeExecLogger key="node-logger" nodeId={props.node?.id!} />);
      }
    }

    if (verificationResultsStore.hasExecResult) {
      content.push(<NodeVerificationLogger key="node-verification-logger" nodeId={props.node?.id!} />);
    }

    return content;
  };

  return (
    <div
      key={props.nodeType}
      className={classNames(['node min-w-130 max-w-150', finalStatus.toLowerCase(), isFocus ? 'is-focus' : '', props.className])}
      onClick={(e) => {
        if (isCancelClick.current) {
          return;
        }
        if (isFocus) {
          e.stopPropagation();
        }
        setActiveNodeId(props.node?.id!);
        props.onClick?.(e as any);
      }}
      onMouseUp={onMouseup}
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
      onMouseDown={(e) => {
        props.onStartDrag?.(e, { ...finalData });
        onMousedown(e);
      }}>
      <div className="flex items-center base-node-wrap flex-1">
        {props.children || <BaseNodeContent nodeName={finalData.nodeName!} nodeType={props.nodeType!} />}
      </div>

      {isInGraph && renderBaseNodeContent()}
      {props.extra}
    </div>
  );
});
